package com.smartplatform.config;

import com.smartplatform.cache.MenuCache;
import com.smartplatform.pojo.SysMenu;
import com.smartplatform.security.handlers.MyUrlAuthenticationFailureHandler;
import com.smartplatform.security.handlers.MyUrlAuthenticationSuccessHandler;
import com.smartplatform.security.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import java.util.List;

/**
 * @Author 咕唧
 * @Date 2022/6/6 18:14
 * @Version 1.0
 * 配置类
 */
@Configuration
@EnableWebSecurity//开启spring security
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    //菜单的缓存数据,里面存放了每个对应的地址和需要的权限
    private MenuCache menuCache;

    private MyUrlAuthenticationSuccessHandler myUrlAuthenticationSuccessHandler;

    private MyUrlAuthenticationFailureHandler myUrlAuthenticationFailureHandler;

    private BCryptPasswordEncoder bCryptPasswordEncoder;

    private MyUserDetailService myUserDetailService;

    @Autowired
    public void setMyUserDetailService(MyUserDetailService myUserDetailService) {
        this.myUserDetailService = myUserDetailService;
    }

    @Autowired
    public void setbCryptPasswordEncoder(BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    @Autowired
    public void setMyUrlAuthenticationFailureHandler(MyUrlAuthenticationFailureHandler myUrlAuthenticationFailureHandler) {
        this.myUrlAuthenticationFailureHandler = myUrlAuthenticationFailureHandler;
    }

    @Autowired
    public void setMyUrlAuthenticationSuccessHandler(MyUrlAuthenticationSuccessHandler myUrlAuthenticationSuccessHandler) {
        this.myUrlAuthenticationSuccessHandler = myUrlAuthenticationSuccessHandler;
    }

    @Autowired
    public void setMenuCache(MenuCache menuCache) {
        this.menuCache = menuCache;
    }

    /**
     * 配置需要的权限和认证请求
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //禁止跨域，不然项目中很多地址都不能访问
        http.csrf().disable();
        //配置地址需要的权限，获取所有的地址列表
        List<SysMenu> allMenus = menuCache.getAllData();
        //必须需要权限的才设置
        allMenus.stream().filter(sysMenu -> !"#".equals(sysMenu.getUrl())
                        && sysMenu.getUrl() != null
                        && !sysMenu.getUrl().equalsIgnoreCase(""))
                .forEach(sysMenu -> {
                    try {
                        http.authorizeRequests()
                                //配置地址
                                .antMatchers(sysMenu.getUrl())
                                //配置权限
                                .hasAuthority(sysMenu.getPerms());
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                });
        //认证所有的请求
        http.authorizeRequests().and()
                //让spring security拦截我们的登录操作,注意这个地址在我们的项目中不要存在,
                // spring security会自动创建这个地址,我们不需要写判断逻辑,
                .formLogin()
                // 我们只需要告诉spring security用户数据库中的密码是什么,spring security会自动帮我们进行逻辑判断
                //那怎么知道是哪个用户的密码,会有专门的一个类来进行处理
                .loginProcessingUrl("/login")
                //当spring security发现登陆成功后跳转什么地址
                //.successForwardUrl("/index2.html")
                //设置自定义的登录成功之后的处理器
                .successHandler(myUrlAuthenticationSuccessHandler)
                //当登录失败的时候跳转到什么地址,一般都是密码错误,应该重新登录,默认就是在登录页面
                //.failureForwardUrl()
                .failureHandler(myUrlAuthenticationFailureHandler)
                //上面的地址需要放行,我们知道登录地址是需要放行的
                .permitAll()
                //配置退出的
                .and().logout()
                //和登录地址一样,不需要我们写这个地址,spring security会自动帮我们处理退出
                .logoutUrl("/logout")
                //成功退出跳转的地址
                .logoutSuccessUrl("/login")
                //退出地址放行
                .permitAll()
                //其他的没有配置的地址都需要登录才可以访问
                .and().authorizeRequests().anyRequest().authenticated();

    }

    /**
     * 忽略的地址, 在这里配置的地址不会经过spring security,
     * 这里面一般忽略的是js css等资源文件,这种本身连登录都不需要的地址就放到这里面来
     * 和permitAll() 不一样,和permitAll是通过security之后放行，这个不通过，直接放行
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/js/**",
                "/layui/**",
                "/websocket/*",
                "/colorcommand/*",
                "/humiture/*"
        );
    }

    /**
     * 告诉spring security 当前登录的用户具体怎么获取到密码以及密码是怎么编码的
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailService).passwordEncoder(bCryptPasswordEncoder);
    }

}
